package com.cjq.springmvc.servlet;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.cjq.springmvc.annotation.CjqController;
import com.cjq.springmvc.annotation.CjqQualifier;
import com.cjq.springmvc.annotation.CjqRequestMapping;
import com.cjq.springmvc.annotation.CjqService;
import com.cjq.springmvc.handlerAdapter.HandlerAdapterService;
import com.cjq.springmvc.util.MyUtil;

@SuppressWarnings("serial")
public class DispatcherServlet extends HttpServlet {

	// 保存所有扫描出来的类
	List<String> classNames = new ArrayList<String>();

	// 保存所有扫描出来实例化的bean
	Map<String, Object> beans = new HashMap<String, Object>();

	// 保存路径与方法的映射
	Map<String, Object> handlerMap = new HashMap<String, Object>();

	private static String HANDLERADAPTER = "cjqHandlerAdapter";

	/**
	 * Default constructor.
	 */
	public DispatcherServlet() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void init() throws ServletException {
		// 1、扫描包
		doScanPackage("com.cjq");
		for (String classname : classNames) {
			System.out.println(classname);
		}

		// 2、实例化
		doInstance();
		for (Map.Entry<String, Object> entry : beans.entrySet()) {
			System.out.println(entry.getKey() + ":" + entry.getValue());
		}

		// 3、依赖注入
		doIoc();

		// 4、建立一个path与method的映射关系
		doHandlerMapping();
		for (Map.Entry<String, Object> entry : handlerMap.entrySet()) {
			System.out.println(entry.getKey() + ":" + entry.getValue());
		}
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			// 获取到请求路径 /springmvc/order/query
			String uri = req.getRequestURI();
			String context = req.getContextPath();
			// 将 "/springmvc/order/query" 去掉"/springmvc"
			String path = uri.replace(context, "");
			Method method = (Method) handlerMap.get(path);
			for (String a : path.split("/")) {
				System.out.println(a);
			}
			Object object = beans.get("/" + path.split("/")[1]);
			
			// 处理器
			HandlerAdapterService ha = (HandlerAdapterService) beans.get(HANDLERADAPTER);
			Object[] args = ha.hand(req, resp, method, beans);
			method.invoke(object, args);
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// 扫描包
	private void doScanPackage(String basePackage) {
		// 扫描编译好的类路径下所有的类
		URL url = this.getClass().getClassLoader().getResource("/" + replaceTo(basePackage));
		String fileStr = url.getFile();
		File file = new File(fileStr);
		// 拿到所有类basePackage下的文件夹
		String[] filesStr = file.list();
		for (String path : filesStr) {
			File filePath = new File(fileStr + path);
			if (filePath.isDirectory()) {
				// 递归调用扫描,如果是路径,继续扫描
				doScanPackage(basePackage + "." + path);
			} else {
				// 如果是class文件则加入List集合(待生成bean)
				classNames.add(basePackage + "." + filePath.getName());
			}
		}
	}

	private String replaceTo(String basePackage) {
		return basePackage.replaceAll("\\.", "/");
	}

	// 实例化
	private void doInstance() {
		if (classNames.size() <= 0) {
			System.out.println("包扫描失败！");
			return;
		}
		// 遍历扫描到的class文件,将需要实例化的类(加了注解的类)进行反射创建对象(像注解就不需要实例化)
		for (String index : classNames) {
			String className = index.replace(".class", "");
			try {
				Class<?> clazz = Class.forName(className);// 拿到class类,用来实例化
				if (clazz.isAnnotationPresent(CjqController.class)) {
					CjqRequestMapping requestMapping = clazz.getAnnotation(CjqRequestMapping.class);
					String key = requestMapping.value();// 得到"/"请求路径,controller使用请求路径作为key
					Object object = clazz.newInstance();
					beans.put(key, object);// 用路径做为key,对应value为实例化对象
				} else if (clazz.isAnnotationPresent(CjqService.class)) {
					// 获取当前clazz类的注解(通过这个注解可得到当前service的id)
					CjqService service = clazz.getAnnotation(CjqService.class);
					String key = service.value();// service使用value值作为key
					if ("".equals(key)) {
						key = MyUtil.lowerFirstCase(clazz.getSimpleName());
					}
					Object object = clazz.newInstance();
					beans.put(key, object);
				}
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	// 初始化IOC容器,依赖注入
	private void doIoc() {
		if (beans.size() <= 0) {
			System.out.println("类实例化失败！");
			return;
		}
		// 将实例化好的bean遍历
		for (Map.Entry<String, Object> entry : beans.entrySet()) {
			Object object = entry.getValue();// 获取bean实例
			Class<?> clazz = object.getClass();// 获取类,用来判断类里声明了哪些注解(主要是针对控制类里的判断,比如使用了@Autowired
												// @Qualifier,对这些注解进行解析)
			if (clazz.isAnnotationPresent(CjqController.class)) {
				Field[] fields = clazz.getDeclaredFields();
				for (Field field : fields) {
					// 判断字段是否声明了自动装配（依赖注入）注解，比如@Autrowired @Qualifier
					if (field.isAnnotationPresent(CjqQualifier.class)) {
						try {
							CjqQualifier qualifier = field.getAnnotation(CjqQualifier.class);
							// 拿到@CjqQualifier("orderServiceImpl")里的指定要注入的bean名字"orderServiceImpl"
							String value = qualifier.value();
							field.setAccessible(true);
							// 从MAP容器中获取"ServiceImpl"对应的bean,并注入实例控制层bean,解决依赖注入
							field.set(object, beans.get(value));
						} catch (IllegalArgumentException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (IllegalAccessException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}
		}
	}

	// 建立一个path与method的映射关系
	private void doHandlerMapping() {
		if (beans.size() <= 0) {
			System.out.println("类实例化失败！");
			return;
		}
		// 将实例化好的bean遍历
		for (Map.Entry<String, Object> entry : beans.entrySet()) {
			Object object = entry.getValue();// 获取bean实例
			Class<?> clazz = object.getClass();// 获取类,用来判断类里声明了哪些注解(主要是针对控制类里的判断,比如使用了@Autowired
												// @Qualifier,对这些注解进行解析)
			if (clazz.isAnnotationPresent(CjqController.class)) {
				CjqRequestMapping controllerRequestMapping = clazz.getAnnotation(CjqRequestMapping.class);
				// 获取Controller类上面的CjqRequestMapping注解里的请求路径
				String controllerRm = controllerRequestMapping.value();
				// 获取控制类里的所有方法
				Method[] methods = clazz.getDeclaredMethods();
				for (Method method : methods) {
					// 判断哪些方法上使用CjqRequestMapping路径注解
					if (method.isAnnotationPresent(CjqRequestMapping.class)) {
						CjqRequestMapping methodRequestMapping = method.getAnnotation(CjqRequestMapping.class);
						String methodRm = methodRequestMapping.value();
						// 把方法上与路径建立映射关系( /order/query--->public void
						// com.cjq.springmvc.controller.OrderController.query )
						handlerMap.put(controllerRm + methodRm, method);
					}
				}
			}
		}
	}

}
